Input and output (I/O) is an essential element of computer operating systems and refers to operations that occur between a computer and external devices, such as printers, storage devices, and other computers. I/O also refers to internal computer operations, such as communication between a processor in the computer and internal disks, memory, and so on.
There are two main categories of I/O: synchronous and asynchronous. In synchronous I/O, when an application calls an I/O operation, the application is suspended until the I/O operation is complete, which is typically signaled by an “interrupt” signal transmitted to the application that requested the I/O operation. In asynchronous I/O, on the other hand, when an application calls an I/O operation, the application is free to perform other tasks while the I/O is being completed. Asynchronous I/O is thus very useful in that it, for example, allows an application to read data being input from a keyboard, while writing data onto a computer screen, and maybe also reading data from a disk drive or memory.
Transactions typically refer to asynchronous read or write operations, as well as interactions between an application and a kernel that requires an asynchronous completion notification. The asynchronous completion notifications can generally be referred to as events.
In a computer operating system, whenever an event occurs, the event is placed in an event queue. Event queues can typically accommodate events generated from several disjoint sources, such as asynchronous I/O, timers, user-defined events, file descriptor events, and so on. Applications can access the event queues through event ports to retrieve events that are in the event queues. The event ports may be physical addresses on a computer or computer device, or mapped locations in the computer's memory. Event ports (and the corresponding event queues) are generally only created on demand.
Typically there is one event queue for every application thread. A thread generally refers to a part of an application program that can run independently and along with other threads to accomplish a task. A number of computer operating systems support multiple threads. Each thread shares the same address space, descriptors, and other resources within an application process, but has its own program counter for execution. A multi-threaded application process can be used for I/O operations that happen on the same descriptor or on different descriptors. Due to the high speed of processors, in a multi-threaded environment, several I/O operations often appear to occur simultaneously.
Occasionally situations occur where several threads or processes that are working on different tasks need to be triggered synchronously by another thread or process. A common way to achieve this is to use signals. However, signals come with a number of drawbacks, for example, signals carry little or no data from the sender of the signal to the receiver of the signal, a signal may interrupt the receiver of the signal in an undefined state, tasks that can be performed within a signal handler are limited, synchronization problems can arise because of user locks, and so on.
From time to time, there is also a need to synchronize several threads or processes, particularly when multiple processes compete for the same operating system resources. Conventionally, such synchronizations are performed using semaphore operations. A semaphore is a value in a designated place in an operating system (or kernel) storage that each process or thread can check and change. Depending on the value found when checking the semaphore, the process can either use the resource or will find that the resource is already in use and that the process must wait for some period before trying again. Semaphores can either be binary or have additional values. Typically, a process using a semaphore checks the value and then, if the process can use the resource, changes the value to reflect this so that subsequent threads or processes using the semaphore will know to wait. Waiting processes or threads cannot perform any tasks while they are waiting, so the use of semaphores may lead to noticeable performance degradation.